{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1e112198",
   "metadata": {},
   "source": [
    "### Diffusion Model using U-Net with Time Embeddings\n",
    "\n",
    "This is an implementation of a U-Net architecture conditioned on time embeddings for diffusion tasks. The model takes in noisy data and predicts the noise component, allowing for the generation of clean samples through a denoising process."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "22bfe034",
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "import os\n",
    "\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "from torch.utils.data import DataLoader\n",
    "from torchvision import datasets, transforms, utils"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fd91fc93",
   "metadata": {},
   "source": [
    "### Sinusoidal Time Embeddings and U-Net Model\n",
    "\n",
    "We implement sinusoidal time embeddings and a U-Net architecture that incorporates these embeddings to condition the model on time.\n",
    "\n",
    "Note that the model input is a concatenation of the data point and the time embedding. The output is the predicted noise component at that time step."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "62cbd176",
   "metadata": {},
   "outputs": [],
   "source": [
    "# ----------------------------\n",
    "# Utilities: time embedding\n",
    "# ----------------------------\n",
    "class SinusoidalPosEmb(nn.Module):\n",
    "    def __init__(self, dim):\n",
    "        super().__init__()\n",
    "        self.dim = dim\n",
    "\n",
    "    def forward(self, t):  # t shape: (B,)\n",
    "        device = t.device\n",
    "        half = self.dim // 2\n",
    "        emb = torch.exp(torch.arange(half, device=device) * -(math.log(10000) / (half - 1)))\n",
    "        emb = t[:, None] * emb[None, :]  # (B, half)\n",
    "        emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=-1)\n",
    "        if self.dim % 2 == 1:  # odd dim\n",
    "            emb = F.pad(emb, (0, 1))\n",
    "        return emb  # (B, dim)\n",
    "\n",
    "# ----------------------------\n",
    "# Small U-Net for 1x28x28 images\n",
    "# ----------------------------\n",
    "def conv_block(in_c, out_c, time_emb_dim=None):\n",
    "    layers = [\n",
    "        nn.Conv2d(in_c, out_c, kernel_size=3, padding=1),\n",
    "        nn.GroupNorm(8, out_c),\n",
    "        nn.SiLU(),\n",
    "    ]\n",
    "    if time_emb_dim is not None:\n",
    "        # add time-conditioned bias after conv\n",
    "        layers.append(TimeCondition(out_c, time_emb_dim))\n",
    "    return nn.Sequential(*layers)\n",
    "\n",
    "class TimeCondition(nn.Module):\n",
    "    def __init__(self, channels, time_emb_dim):\n",
    "        super().__init__()\n",
    "        self.proj = nn.Linear(time_emb_dim, channels)\n",
    "\n",
    "    def forward(self, x):\n",
    "        # this module will never be used stand-alone in my conv_block chain\n",
    "        raise NotImplementedError(\"TimeCondition is used in UNet forward directly.\")\n",
    "\n",
    "class UNetSimple(nn.Module):\n",
    "    def __init__(self, time_emb_dim=128):\n",
    "        super().__init__()\n",
    "        self.time_emb = SinusoidalPosEmb(time_emb_dim)\n",
    "        self.time_mlp = nn.Sequential(\n",
    "            nn.Linear(time_emb_dim, time_emb_dim * 2),\n",
    "            nn.SiLU(),\n",
    "            nn.Linear(time_emb_dim * 2, time_emb_dim),\n",
    "        )\n",
    "\n",
    "        # Encoder\n",
    "        self.inc = nn.Sequential(\n",
    "            nn.Conv2d(1, 32, kernel_size=3, padding=1),\n",
    "            nn.GroupNorm(8, 32),\n",
    "            nn.SiLU(),\n",
    "        )\n",
    "        self.down1 = nn.Sequential(\n",
    "            nn.Conv2d(32, 64, kernel_size=4, stride=2, padding=1),  # 28->14\n",
    "            nn.GroupNorm(8, 64),\n",
    "            nn.SiLU(),\n",
    "        )\n",
    "        self.down2 = nn.Sequential(\n",
    "            nn.Conv2d(64, 128, kernel_size=4, stride=2, padding=1),  # 14->7\n",
    "            nn.GroupNorm(8, 128),\n",
    "            nn.SiLU(),\n",
    "        )\n",
    "\n",
    "        # Bottleneck\n",
    "        self.mid = nn.Sequential(\n",
    "            nn.Conv2d(128, 128, 3, padding=1),\n",
    "            nn.GroupNorm(8, 128),\n",
    "            nn.SiLU(),\n",
    "            nn.Conv2d(128, 128, 3, padding=1),\n",
    "            nn.GroupNorm(8, 128),\n",
    "            nn.SiLU(),\n",
    "        )\n",
    "\n",
    "        # Decoder\n",
    "        self.up2 = nn.Sequential(\n",
    "            nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1),  # 7->14\n",
    "            nn.GroupNorm(8, 64),\n",
    "            nn.SiLU(),\n",
    "        )\n",
    "        self.up1 = nn.Sequential(\n",
    "            nn.ConvTranspose2d(128, 32, kernel_size=4, stride=2, padding=1),  # 14->28\n",
    "            nn.GroupNorm(8, 32),\n",
    "            nn.SiLU(),\n",
    "        )\n",
    "\n",
    "        self.outc = nn.Sequential(\n",
    "            nn.Conv2d(64, 32, kernel_size=3, padding=1),\n",
    "            nn.GroupNorm(8, 32),\n",
    "            nn.SiLU(),\n",
    "            nn.Conv2d(32, 1, kernel_size=3, padding=1),\n",
    "        )\n",
    "\n",
    "        # small MLP to inject time into features at each stage\n",
    "        self.time_proj0 = nn.Linear(time_emb_dim, 32)\n",
    "        self.time_proj1 = nn.Linear(time_emb_dim, 64)\n",
    "        self.time_proj2 = nn.Linear(time_emb_dim, 128)\n",
    "        self.time_proj_mid = nn.Linear(time_emb_dim, 128)\n",
    "        self.time_proj_up1 = nn.Linear(time_emb_dim, 128)\n",
    "        self.time_proj_up2 = nn.Linear(time_emb_dim, 64)\n",
    "\n",
    "    def forward(self, x, t):\n",
    "        \"\"\"\n",
    "        x: (B, 1, 28, 28)\n",
    "        t: (B,) in [0,1]\n",
    "        output: predicted velocity v(x_t, t) same shape as x\n",
    "        \"\"\"\n",
    "        B = x.shape[0]\n",
    "        te = self.time_emb(t)         # (B, time_dim)\n",
    "        te = self.time_mlp(te)        # (B, time_dim)\n",
    "\n",
    "        # encode\n",
    "        e0 = self.inc(x)              # (B,32,28,28)\n",
    "        e0 = e0 + self.time_proj0(te)[:, :, None, None]\n",
    "        e1 = self.down1(e0)           # (B,64,14,14)\n",
    "        e1 = e1 + self.time_proj1(te)[:, :, None, None]\n",
    "        e2 = self.down2(e1)           # (B,128,7,7)\n",
    "        e2 = e2 + self.time_proj2(te)[:, :, None, None]\n",
    "\n",
    "        # mid\n",
    "        m = self.mid(e2)\n",
    "        m = m + self.time_proj_mid(te)[:, :, None, None]\n",
    "        # decode\n",
    "        d2 = self.up2(m)              # (B,64,14,14)\n",
    "        d2 = torch.cat([d2, e1], dim=1)  # (B,128,14,14)\n",
    "\n",
    "        d2 = d2 + self.time_proj_up1(te)[:, :, None, None]\n",
    "\n",
    "        d1 = self.up1(d2)             # (B,32,28,28)\n",
    "        d1 = torch.cat([d1, e0], dim=1)  # (B,64,28,28)\n",
    "        d1 = d1 + self.time_proj_up2(te)[:, :, None, None]\n",
    "\n",
    "        out = self.outc(d1)           # (B,1,28,28)  => velocity\n",
    "        return out\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06637c55",
   "metadata": {},
   "source": [
    "### Training Diffusion Model\n",
    "\n",
    "We use the DDPM framework to train the diffusion model on the MNIST dataset. The training loop includes sampling and saving generated images at the end of each epoch.\n",
    "\n",
    "At each step, we generate noisy samples based on the current time step and train the model to predict the noise component.\n",
    "\n",
    "The noisy sample equation used is:\n",
    "\n",
    "$$x_t = \\sqrt{\\bar{\\alpha}_t} x_0 + \\sqrt{1 - \\bar{\\alpha}_t} \\epsilon$$\n",
    "\n",
    "where $x_0$ is the original data, $\\epsilon$ is standard Gaussian noise, and $\\bar{\\alpha}_t$ is derived from the noise schedule.\n",
    "\n",
    "During inference, we iteratively denoise samples starting from pure noise to generate clean images. The equation for denoising is:\n",
    "$$x_{t-1} = \\frac{1}{\\sqrt{\\alpha_t}} \\left( x_t - \\frac{1 - \\alpha_t}{\\sqrt{1 - \\bar{\\alpha}_t}} \\epsilon_\\theta(x_t, t) \\right) + \\sigma_t z$$\n",
    "\n",
    "where $z$ is standard Gaussian noise and $\\sigma_t$ controls the amount of noise added at each step.\n",
    "\n",
    "In this example, we use the following equation for denoising:\n",
    "$$x_{t-1} = \\frac{1}{\\sqrt{\\alpha_t}} \\left( x_t - \\frac{\\beta_t}{\\sqrt{1 - \\bar{\\alpha}_t}} \\epsilon_\\theta(x_t, t) \\right) + \\sqrt{\\beta_t} z$$\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "a1416644",
   "metadata": {},
   "outputs": [],
   "source": [
    "# -------------------------------------\n",
    "# Diffusion functions\n",
    "# -------------------------------------\n",
    "def make_beta_schedule(T, start=1e-4, end=0.02):\n",
    "    return torch.linspace(start, end, T)\n",
    "\n",
    "\n",
    "class DDPM:\n",
    "    def __init__(self, model, T=1000, device=\"cuda\"):\n",
    "        self.model = model\n",
    "        self.T = T\n",
    "        self.device = device\n",
    "\n",
    "        betas = make_beta_schedule(T).to(device)\n",
    "        self.betas = betas\n",
    "        self.alphas = 1.0 - betas\n",
    "        self.alpha_cum = torch.cumprod(self.alphas, dim=0)\n",
    "\n",
    "        self.sqrt_alpha_cum = torch.sqrt(self.alpha_cum)\n",
    "        self.sqrt_1m_alpha_cum = torch.sqrt(1 - self.alpha_cum)\n",
    "\n",
    "    # q(x_t | x_0)\n",
    "    def q_sample(self, x0, t, noise=None):\n",
    "        if noise is None:\n",
    "            noise = torch.randn_like(x0)\n",
    "        return (\n",
    "            extract(self.sqrt_alpha_cum, t, x0.shape) * x0 +\n",
    "            extract(self.sqrt_1m_alpha_cum, t, x0.shape) * noise\n",
    "        )\n",
    "\n",
    "    # sampling reverse\n",
    "    @torch.no_grad()\n",
    "    def sample(self, n=64):\n",
    "        x = torch.randn(n, 1, 28, 28, device=self.device)\n",
    "\n",
    "        for i in reversed(range(self.T)):\n",
    "            t = torch.full((n,), i, device=self.device, dtype=torch.long)\n",
    "            eps = self.model(x, t / self.T)\n",
    "\n",
    "            beta = self.betas[i]\n",
    "            alpha = self.alphas[i]\n",
    "            alpha_cum = self.alpha_cum[i]\n",
    "\n",
    "            # DDPM reverse step\n",
    "            if i > 0:\n",
    "                noise = torch.randn_like(x)\n",
    "            else:\n",
    "                noise = 0\n",
    "\n",
    "            x = (1 / torch.sqrt(alpha)) * (x - beta / torch.sqrt(1 - alpha_cum) * eps) + torch.sqrt(beta) * noise\n",
    "\n",
    "        return x\n",
    "\n",
    "\n",
    "# extract a[t] -> reshape to x shape\n",
    "def extract(a, t, x_shape):\n",
    "    return a[t].view(-1, 1, 1, 1)\n",
    "\n",
    "\n",
    "# -------------------------------------\n",
    "# Training loop\n",
    "# -------------------------------------\n",
    "def train_ddpm(\n",
    "    device=\"cuda\",\n",
    "    epochs=10,\n",
    "    batch_size=128,\n",
    "    lr=2e-4,\n",
    "    T=1000,\n",
    "    model_save_dir=\"models_ddpm\",\n",
    "    sample_dir=\"samples_ddpm\",\n",
    "):\n",
    "    os.makedirs(model_save_dir, exist_ok=True)\n",
    "    os.makedirs(sample_dir, exist_ok=True)\n",
    "\n",
    "    # MNIST [-1, 1]\n",
    "    transform = transforms.Compose([\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize((0.5,), (0.5,))\n",
    "    ])\n",
    "\n",
    "    dataset = datasets.MNIST(root=\"~/data\", train=True, download=True, transform=transform)\n",
    "    loader = DataLoader(dataset, batch_size=batch_size, shuffle=True, num_workers=2, pin_memory=True)\n",
    "\n",
    "    model = UNetSimple().to(device)\n",
    "    ddpm = DDPM(model, T=T, device=device)\n",
    "    opt = torch.optim.Adam(model.parameters(), lr=lr)\n",
    "    mse = nn.MSELoss()\n",
    "\n",
    "    for ep in range(1, epochs + 1):\n",
    "        model.train()\n",
    "        total_loss = 0\n",
    "\n",
    "        for x0, _ in loader:\n",
    "            x0 = x0.to(device)\n",
    "\n",
    "            # sample t uniformly\n",
    "            b = x0.shape[0]\n",
    "            t = torch.randint(0, T, (b,), device=device).long()\n",
    "            t_float = t.float() / T\n",
    "\n",
    "            noise = torch.randn_like(x0)\n",
    "            xt = ddpm.q_sample(x0, t, noise)\n",
    "\n",
    "            # network predicts noise\n",
    "            pred_noise = model(xt, t_float)\n",
    "            loss = mse(pred_noise, noise)\n",
    "\n",
    "            opt.zero_grad()\n",
    "            loss.backward()\n",
    "            opt.step()\n",
    "\n",
    "            total_loss += loss.item()\n",
    "\n",
    "        print(f\"Epoch {ep}: loss={total_loss/len(loader):.6f}\")\n",
    "\n",
    "        # sample and save\n",
    "        model.eval()\n",
    "        with torch.no_grad():\n",
    "            samples = ddpm.sample(n=16)\n",
    "            samples = (samples.clamp(-1, 1) + 1) / 2  # to [0,1]\n",
    "            image_path = os.path.join(sample_dir, f\"ep{ep}.png\")\n",
    "            utils.save_image(samples, image_path, nrow=4)\n",
    "            print(f\"Saved sample images to {image_path}\")\n",
    "\n",
    "        torch.save(model.state_dict(), os.path.join(model_save_dir, f\"ddpm_ep{ep}.pt\"))\n",
    "        checkpoint_path = os.path.join(model_save_dir, f\"ddpm_ep{ep}.pt\")\n",
    "        print(f\"Saved samples and checkpoint for epoch {ep} at {checkpoint_path}\")\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "764cad82",
   "metadata": {},
   "source": [
    "### Testing the Model with Sample Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "fdab8324",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using device: cuda\n",
      "Output shape: torch.Size([1, 1, 28, 28])\n",
      "Number of parameters in the model: 0.81 Million\n"
     ]
    }
   ],
   "source": [
    "device = 'cuda' if torch.cuda.is_available() else 'cpu'\n",
    "print(\"Using device:\", device)\n",
    "model = UNetSimple(time_emb_dim=128).to(device)\n",
    "#print(model)\n",
    "data = torch.randn(1, 1, 28, 28).to(device)\n",
    "output = model(data, torch.tensor([0.5], device=device))\n",
    "print(\"Output shape:\", output.shape)\n",
    "# number of parameters\n",
    "num_params = sum(p.numel() for p in model.parameters())\n",
    "# in Millions\n",
    "print(f\"Number of parameters in the model: {num_params / 1e6:.2f} Million\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e6c83e86",
   "metadata": {},
   "source": [
    "### Training Flow Matching Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "2ba2c253",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1: loss=0.078517\n",
      "Saved sample images to samples_ddpm/ep1.png\n",
      "Saved samples and checkpoint for epoch 1 at models_ddpm/ddpm_ep1.pt\n",
      "Epoch 2: loss=0.039767\n",
      "Saved sample images to samples_ddpm/ep2.png\n",
      "Saved samples and checkpoint for epoch 2 at models_ddpm/ddpm_ep2.pt\n",
      "Epoch 3: loss=0.034194\n",
      "Saved sample images to samples_ddpm/ep3.png\n",
      "Saved samples and checkpoint for epoch 3 at models_ddpm/ddpm_ep3.pt\n",
      "Epoch 4: loss=0.031734\n",
      "Saved sample images to samples_ddpm/ep4.png\n",
      "Saved samples and checkpoint for epoch 4 at models_ddpm/ddpm_ep4.pt\n",
      "Epoch 5: loss=0.030534\n",
      "Saved sample images to samples_ddpm/ep5.png\n",
      "Saved samples and checkpoint for epoch 5 at models_ddpm/ddpm_ep5.pt\n",
      "Epoch 6: loss=0.029114\n",
      "Saved sample images to samples_ddpm/ep6.png\n",
      "Saved samples and checkpoint for epoch 6 at models_ddpm/ddpm_ep6.pt\n",
      "Epoch 7: loss=0.028628\n",
      "Saved sample images to samples_ddpm/ep7.png\n",
      "Saved samples and checkpoint for epoch 7 at models_ddpm/ddpm_ep7.pt\n",
      "Epoch 8: loss=0.027817\n",
      "Saved sample images to samples_ddpm/ep8.png\n",
      "Saved samples and checkpoint for epoch 8 at models_ddpm/ddpm_ep8.pt\n",
      "Epoch 9: loss=0.027453\n",
      "Saved sample images to samples_ddpm/ep9.png\n",
      "Saved samples and checkpoint for epoch 9 at models_ddpm/ddpm_ep9.pt\n",
      "Epoch 10: loss=0.026768\n",
      "Saved sample images to samples_ddpm/ep10.png\n",
      "Saved samples and checkpoint for epoch 10 at models_ddpm/ddpm_ep10.pt\n"
     ]
    }
   ],
   "source": [
    "train_ddpm(\n",
    "        device=\"cuda\" if torch.cuda.is_available() else \"cpu\",\n",
    "        epochs=10,\n",
    "        batch_size=64,\n",
    "        lr=2e-4,\n",
    "        T=1000,\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41933be8",
   "metadata": {},
   "source": [
    "### Display Sample Predictions After Training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "e032a977",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# display samples in notebook\n",
    "from IPython.display import Image, display\n",
    "display(Image(filename='samples_ddpm/ep10.png'))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a31d6ace",
   "metadata": {},
   "source": [
    "#### First Epoch Predictions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "26908053",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(Image(filename='samples_ddpm/ep1.png'))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
